package com.xuecheng.ucenter.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.mapper.XcUserRoleMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.model.po.XcUserRole;
import com.xuecheng.ucenter.service.AuthService;
import com.xuecheng.ucenter.service.WxAuthService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;

//微信扫码认证
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {

    @Autowired
    XcUserMapper xcUserMapper;
    @Value("${weixin.appid}")
    String appid;
    @Value("${weixin.secret}")
    String secret;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    XcUserRoleMapper xcUserRoleMapper;
    @Autowired
    WxAuthServiceImpl currentProxy;//代理对象,保证事物的一致性

    @Override//统一认证入口
    public XcUserExt execute(AuthParamsDto authParamsDto) {
        String username = authParamsDto.getUsername();
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
        if (xcUser == null){
            throw new RuntimeException("用户不存在");
        }
        XcUserExt xcUserExt = new XcUserExt();
        BeanUtils.copyProperties(xcUser,xcUserExt);
        return xcUserExt;
    }

    @Override
    public XcUser wxAuth(String code) {
//  申请令牌
        Map<String, String> access_token_map = getAccess_token(code);
//         访问令牌
        String access_token = access_token_map.get("access_token");
        String openid = access_token_map.get("openid");
//  携带令牌查询用户信息
        Map<String, String> userinfo = getUserinfo(access_token, openid);
//  保存用户信息到数据库
        XcUser xcUser = currentProxy.addWxUser(userinfo);
        return xcUser;
    }

    /**
     * http请求方式: GET 返回的信息
     * https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code
     * 申请访问令牌,响应示例
     {
     "access_token":"ACCESS_TOKEN",接口调用凭证
     "expires_in":7200, 凭证超时时间秒
     "refresh_token":"REFRESH_TOKEN",
     "openid":"OPENID",授权用户的唯一标识
     "scope":"SCOPE",
     "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     }
     */
    private Map<String,String> getAccess_token(String code){
        String url_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
        String url = String.format(url_template, appid, secret, code);
//        todo 后端的访问服务的方式
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, null, String.class);
        String body = exchange.getBody();
        Map<String,String> map = JSON.parseObject(body, Map.class);
        return map;
    }

    /**获取用户信息，示例如下：
     * https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s
     * 返回结果:
     {
     "openid":"OPENID", 普通用户的标识，对当前开发者帐号唯一
     "nickname":"NICKNAME",
     "sex":1, 1为男性，2为女性
     "province":"PROVINCE",
     "city":"CITY", 普通用户个人资料填写的省份
     "country":"COUNTRY", 普通用户个人资料填写的城市
     用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空
     "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
     户特权信息，json数组，如微信沃卡用户为（chinaunicom）
     "privilege":[
     "PRIVILEGE1",
     "PRIVILEGE2"
     ],
     用户统一标识。针对一个微信开放平台帐号下的应用，同一用户的 unionid 是唯一的。
     "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
     }
     */
    private Map<String,String> getUserinfo(String access_token,String openid){
        String url_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";
        String url = String.format(url_template, access_token,openid);
//        todo 后端的访问服务的方式
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//        改变编码格式,中文会乱码
        String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
        Map<String,String> map = JSON.parseObject(result, Map.class);
        return map;
    }

    @Transactional
    public XcUser addWxUser(Map<String,String> userInfo_map){
        String unionid = userInfo_map.get("unionid");
        //根据unionid查询数据库
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getWxUnionid, unionid));
        if(xcUser!=null){
            return xcUser;
        }
        xcUser = new XcUser();
        String userId = UUID.randomUUID().toString();
        xcUser.setId(userId);
        xcUser.setWxUnionid(unionid);
        //记录从微信得到的昵称
        xcUser.setNickname(userInfo_map.get("nickname"));
        xcUser.setUserpic(userInfo_map.get("headimgurl"));
        xcUser.setName(userInfo_map.get("nickname"));
        xcUser.setUsername(unionid);
        xcUser.setPassword(unionid);
        xcUser.setUtype("101001");//学生类型
        xcUser.setStatus("1");//用户状态
        xcUser.setCreateTime(LocalDateTime.now());
        xcUserMapper.insert(xcUser);
//        用户关系表
        XcUserRole xcUserRole = new XcUserRole();
        xcUserRole.setId(UUID.randomUUID().toString());
        xcUserRole.setUserId(userId);
        xcUserRole.setRoleId("17");//学生角色
        xcUserRole.setCreateTime(LocalDateTime.now());
        xcUserRoleMapper.insert(xcUserRole);
        return xcUser;
    }
}
//todo 我们没有接入公网,不过因为我们的登陆服务就在这个电脑上,所以我们可以这样写
//分步拆解：微信的回调请求如何 “找到你本地服务”
//我们把 “微信发 code 到你本地服务” 的过程，拆成 3 个关键步骤，每个步骤都对应文档中的配置：
//步骤 1：微信按 redirect_uri 发请求 —— 目标是 “域名” 而非 “公网 IP”
//用户扫码确认后，微信服务器会严格按照你前端配置的 redirect_uri（http://tjxt-user-t.itheima.net/api/auth/wxLogin）发送回调请求，携带 code 和 state。此时微信的逻辑是：“我要把 code 送到 tjxt-user-t.itheima.net 这个域名的 /api/auth/wxLogin 路径下”—— 但微信不知道这个域名对应的 IP 是多少，它会像你访问网站一样，先 “解析域名对应的 IP”。
//步骤 2：hosts 配置 “强制” 域名解析到你本地 IP
//文档要求在 hosts 文件中添加 127.0.0.1 tjxt-user-t.itheima.net，这是整个 “本地回调” 的核心：
//hosts 文件的作用：是你电脑的 “本地 DNS 字典”—— 当你的电脑要访问某个域名时，会先查 hosts，再查公网 DNS；
//配置后效果：当微信的回调请求到达你的电脑时（因为你用这台电脑测试，请求会先经过你的电脑网络），你的电脑会直接告诉微信：“tjxt-user-t.itheima.net 对应的 IP 是 127.0.0.1”（127.0.0.1 是 “本地回环地址”，代表 “当前这台电脑自己”）；
//关键：这一步绕开了公网 DNS—— 即使 tjxt-user-t.itheima.net 在公网上没有注册，你的电脑也能 “私自” 把它映射到自己的 IP，让微信的回调请求不会跑到公网，而是直接指向你本地。
//步骤 3：Nginx 接收请求，转发到本地后端服务
//文档中配置 Nginx 监听 80 端口、绑定 tjxt-user-t.itheima.net 域名，作用是 “接住微信的回调请求，并转交给你的后端服务”：
//微信的回调请求（目标 IP：127.0.0.1，端口：80，路径：/api/auth/wxLogin）会先到达你本地的 Nginx（因为 Nginx 正在监听 80 端口，且绑定了该域名）；
//Nginx 会根据配置，把这个请求 “转发” 到你本地运行的后端服务（比如你的后端服务在 127.0.0.1:8080 端口）；
//最终：后端的 WxLoginController 就能接收到微信传递的 code，完成后续的登录逻辑（比如用 code 换 access_token、查用户信息）。
